package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.AuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ApCollectionService;
import com.heima.article.service.AuthorService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.constants.message.HotArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.behavior.BehaviorFeign;
import com.heima.feigns.user.UserFeign;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.dtos.ArticleInfoDto;
import com.heima.model.article.mess.ArticleVisitStreamMess;
import com.heima.model.article.pojos.*;
import com.heima.model.article.vo.HotArticleVo;
import com.heima.model.behavior.dtos.ApArticleRelationDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.common.threadlocal.AppThreadLocalUtils;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserFollow;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.weaver.ast.Var;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;


@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    @Autowired
    AuthorMapper authorMapper;
    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    ApArticleContentMapper apArticleContentMapper;
    @Autowired
    ApArticleMapper apArticleMapper;


    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        // 1. 校验参数
        if(articleDto == null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 根据作者名称查询作者信息
        ApAuthor author = authorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, articleDto.getAuthorName()));
        if(author==null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章关联的作者信息不存在");
        }
        articleDto.setAuthorId(Long.valueOf(author.getId()));
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(articleDto,apArticle);
        // 3. 判断文章id是否存在
        if(apArticle.getId() == null){
            // 3.1 不存在   添加文章
            apArticle.setComment(0); // 评论
            apArticle.setLikes(0); // 点赞
            apArticle.setCollection(0); // 收藏
            apArticle.setViews(0); // 查看
            save(apArticle); // 保存
        }else {
            // 3.2 存在
            //     先根据文章ID查询   没有抛出异常 修改文章
            ApArticle one = getById(apArticle.getId());
            if(one == null){
                throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"文章信息不存在");
            }
            //     根据文章id 删除配置信息
            apArticleConfigMapper.delete(Wrappers.<ApArticleConfig>lambdaQuery()
                    .eq(ApArticleConfig::getArticleId,apArticle.getId()));
            //     根据文章id 删除内容信息
            apArticleContentMapper.delete(Wrappers.<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId,apArticle.getId()));
            //     修改文章内容
            updateById(apArticle);
        }
        // 4. 添加文章内容信息
        ApArticleContent apArticleContent = new ApArticleContent();
        apArticleContent.setArticleId(apArticle.getId());
        apArticleContent.setContent(articleDto.getContent());
        apArticleContentMapper.insert(apArticleContent);
        // 5. 添加文章配置信息
        ApArticleConfig apArticleConfig = new ApArticleConfig();
        apArticleConfig.setArticleId(apArticle.getId());
        apArticleConfig.setIsComment(true);
        apArticleConfig.setIsForward(true);
        apArticleConfig.setIsDown(false);
        apArticleConfig.setIsDelete(false);
        apArticleConfigMapper.insert(apArticleConfig);
        // 6. 封装返回结果,携带文章id
        return ResponseResult.okResult(apArticle.getId());
    }

    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto dto) {
        // 1. 校验参数
        if(dto.getSize() == null || dto.getSize() < 1 || dto.getSize() > 50){
            dto.setSize(10);
        }
        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }
        if (StringUtils.isBlank(dto.getTag())) {
            dto.setTag(ArticleConstants.DEFAULT_TAG);
        }
        // 2. 调用mapper对应方法查询列表
        List<ApArticle> apArticles = apArticleMapper.loadArticleList(dto, loadtype);
        // 3. 封装返回结果
        ResponseResult responseResult = ResponseResult.okResult(apArticles);
        // 4. 加上图片访问前缀
        responseResult.setHost(webSite);
        return responseResult;
    }

    @Override
    public ResponseResult loadArticleInfo(ArticleInfoDto dto) {
        // 1. 参数校验  articleId
        if(dto.getArticleId() == null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID,"文章id不能为空");
        }
        // 2. 查询 文章内容 content
        ApArticleContent content = apArticleContentMapper.selectOne(Wrappers.<ApArticleContent>lambdaQuery()
                .eq(ApArticleContent::getArticleId, dto.getArticleId()));
        if(content == null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"文章信息不存在");
        }
        // 3. 文章配置  config 判断不能是删除状态  不能是下架状态
        ApArticleConfig config = apArticleConfigMapper.selectOne(Wrappers.<ApArticleConfig>lambdaQuery()
                .eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if(config == null){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"文章配置信息不存在");
        }
        if(config.getIsDelete() || config.getIsDown()){
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"该文章已经下架");
        }
        // 4. 封装返回结果  Map ( content: 内容      config: 配置  )
        Map map = new HashMap<>();
        map.put("content",content);
        map.put("config",config);
        return ResponseResult.okResult(map);
    }

    @Autowired
    UserFeign userFeign;

    @Autowired
    AuthorService authorService;

    @Autowired
    BehaviorFeign behaviorFeign;

    @Autowired
    ApCollectionService apCollectionService;

    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto dto) {
        Map resultMap = new HashMap();
        resultMap.put("isfollow",false);
        resultMap.put("iscollection",false);
        resultMap.put("islike",false);
        resultMap.put("isunlike",false);
        // 1. 检查参数 (文章id  作者id )
        if(dto.getArticleId() == null || dto.getAuthorId() == null){
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 根据userFeign远程调用接口 查询 关注信息
        // 查询当前用户是否登录，如果登录了 查询关注信息
        ApUser user = AppThreadLocalUtils.getUser();
        if(user != null){
            ApAuthor author = authorService.getById(dto.getAuthorId());
            if(author == null){
                throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST,"关注的作者信息不存在");
            }
            ApUserFollow apUserFollow = userFeign.findByUserIdAndFollowId(user.getId(), author.getUserId());
            if (apUserFollow != null) {
                resultMap.put("isfollow",true);
            }
        }
        // 3. 根据behaviorFeign远程调用结果  查询  点赞 和 不喜欢的信息
        ApArticleRelationDto relationDto = new ApArticleRelationDto();
        relationDto.setArticleId(dto.getArticleId());
        if(user != null){
            relationDto.setType((short)1);
            relationDto.setEntryId(user.getId());
        }else {
            relationDto.setType((short)0);
            relationDto.setEntryId(dto.getEquipmentId());
        }
        // 从行为微服务中  获取 是否点赞   是否不喜欢
        Map behaviorMap = behaviorFeign.findApArticleRelation(relationDto);
        if(behaviorMap!=null){ // islike  isunlike
            resultMap.putAll(behaviorMap);
            // 实体ID
            Integer entryId = (Integer)resultMap.get("entryId");
            // 4. 查询是否收藏的信息
            ApCollection one = apCollectionService.getOne(Wrappers.<ApCollection>lambdaQuery()
                    .eq(ApCollection::getEntryId, entryId)
                    .eq(ApCollection::getArticleId, dto.getArticleId()));
            if(one!=null){
                resultMap.put("iscollection",true);
            }
        }
        // 5. 封装成Map 返回给前端
        return ResponseResult.okResult(resultMap);
    }

    /**
     * 更新文章热度
     * @param streamMess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess streamMess) {
        // 1. 根据文章id 查询文章数据
        ApArticle apArticle = getById(streamMess.getArticleId());
        if(apArticle == null){
            log.error("更新文章热度方法失败,未查询到文章信息 ===>"+streamMess.getArticleId());
            return ;
        }
        // 2. 根据流处理结果，更新文章行为数量
        int views = apArticle.getViews()==null? 0 : apArticle.getViews() + (int)streamMess.getView();
        apArticle.setViews(views);
        int comment = apArticle.getComment()==null? 0 : apArticle.getComment() + (int)streamMess.getComment();
        apArticle.setComment(comment);
        int likes = apArticle.getLikes()==null? 0 : apArticle.getLikes() + (int)streamMess.getLike();
        apArticle.setLikes(likes);
        int collections = apArticle.getCollection()==null? 0 : apArticle.getCollection() + (int)streamMess.getCollect();
        apArticle.setCollection(collections);
        updateById(apArticle);
        // 3. 计算文章分值， 今日文章整体热度*3
        Integer score = computeScore(apArticle)*3;
        // 4. 查询对应频道热点文章，替换分值较低热点文章
        updateArticleCache(apArticle,score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE+apArticle.getChannelId());
        // 5. 查询推荐频道热点文章，替换分值较低热点文章
        updateArticleCache(apArticle,score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);
    }
    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if(firstPage){
            // 从redis中获取数据
            String hotArticleJson = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if(StringUtils.isNotBlank(hotArticleJson)){
                List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleJson, HotArticleVo.class);
                ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                responseResult.setHost(webSite);
                return responseResult;
            }
        }
        return load(loadtypeLoadMore,dto);
    }
    @Autowired
    RedisTemplate<String,String> redisTemplate;
    /**
     * 更新热点文章缓存
     * 如果缓存中已经存在该文章 修改对应score
     * 如果缓存中不存在该文章  是否大于30条
     * 小于30条  直接将当前文章加入热点文章
     * 如果大于等于30  当前文章热度分值  是否 大于最后一条分值较低的文章
     * @param apArticle
     * @param score
     * @param cacheKey
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String cacheKey) {
        // 1. 根据缓存key 查询出对应的 热点文章集合
        String hotArticleJson = redisTemplate.opsForValue().get(cacheKey);
        if(StringUtils.isNotBlank(hotArticleJson)){
            // 得到当前redis中 热点文章集合
            List<HotArticleVo> hotArticleVoList = JSONArray.parseArray(hotArticleJson, HotArticleVo.class);
            boolean flag = false;
            // 2. 判断文章是否存在热点文章中，如果存在直接更新分值
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if(hotArticleVo.getId().equals(apArticle.getId())){
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            // 3. 如果不存在直接将文章添加到热点文章列表中
            if(!flag){
                HotArticleVo hotArticleVo = new HotArticleVo();
                BeanUtils.copyProperties(apArticle,hotArticleVo);
                hotArticleVo.setScore(score);
                hotArticleVoList.add(hotArticleVo);
            }
            // 4. 文章列表按照得分降序排序  直接取前30条
            hotArticleVoList = hotArticleVoList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            // 5. 重新缓存到redis中
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleVoList));
        }
    }


    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        // 点赞
        Integer likes = apArticle.getLikes()==null? 0:apArticle.getLikes();
        score += likes * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        // 阅读
        Integer views = apArticle.getViews()==null? 0:apArticle.getViews();
        score += views * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        // 评论
        Integer comments = apArticle.getComment()==null? 0:apArticle.getComment();
        score += comments * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        // 收藏
        Integer collections = apArticle.getCollection()==null? 0:apArticle.getCollection();
        score += collections * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        return score;
    }
    @Value("${file.oss.web-site}")
    String webSite;
}